package com.stars.easyms.schedule.bean;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 批量提交时返回的结果
 *
 * @author guoguifang
 */
public class BatchResult<T> {

    private final List<T> successDatas = new ArrayList<>();

    private final Map<Integer, T> successOrdinalMap = new TreeMap<>();

    private final List<T> failDatas = new ArrayList<>();

    private final Map<Integer, T> failOrdinalMap = new TreeMap<>();

    private final List<Integer> successDataIndexs = new ArrayList<>();

    private final List<Integer> failDataIndexs = new ArrayList<>();

    private final List<Result<T>> resultList = new ArrayList<>();

    private final ReentrantLock successLock = new ReentrantLock();

    private final ReentrantLock failLock = new ReentrantLock();

    public Map<Integer, T> getSuccessOrdinalMap() {
        return this.successOrdinalMap;
    }

    public List<T> getSuccessDatas() {
        this.successLock.lock();
        try {
            if (this.successDatas.size() != this.successOrdinalMap.size()) {
                this.successDatas.clear();
                for (Integer index : this.successOrdinalMap.keySet()) {
                    this.successDatas.add(this.successOrdinalMap.get(index));
                }
            }
            return this.successDatas;
        } finally {
            this.successLock.unlock();
        }
    }

    public List<Integer> getSuccessDataIndexs() {
        this.successLock.lock();
        try {
            if (this.successDataIndexs.size() != this.successOrdinalMap.size()) {
                this.successDataIndexs.clear();
                this.successDataIndexs.addAll(this.successOrdinalMap.keySet());
            }
            return this.successDataIndexs;
        } finally {
            this.successLock.unlock();
        }
    }

    public void addSuccessDatas(Map<Integer, T> successDatas) {
        if (successDatas != null && successDatas.size() > 0) {
            this.successLock.lock();
            try {
                this.successOrdinalMap.putAll(successDatas);
            } finally {
                this.successLock.unlock();
            }
        }
    }

    public Map<Integer, T> getFailOrdinalMap() {
        return this.failOrdinalMap;
    }

    public List<T> getFailDatas() {
        this.failLock.lock();
        try {
            if (this.failDatas.size() != this.failOrdinalMap.size()) {
                this.failDatas.clear();
                for (Integer index : this.failOrdinalMap.keySet()) {
                    this.failDatas.add(this.failOrdinalMap.get(index));
                }
            }
            return this.failDatas;
        } finally {
            this.failLock.unlock();
        }
    }

    public List<Integer> getFailDataIndexs() {
        this.failLock.lock();
        try {
            if (this.failDataIndexs.size() != this.failOrdinalMap.size()) {
                this.failDataIndexs.clear();
                this.failDataIndexs.addAll(this.failOrdinalMap.keySet());
            }
            return this.failDataIndexs;
        } finally {
            this.failLock.unlock();
        }
    }

    public void addFailDatas(Map<Integer, T> failDatas) {
        if (failDatas != null && failDatas.size() > 0) {
            this.failLock.lock();
            try {
                this.failOrdinalMap.putAll(failDatas);
            } finally {
                this.failLock.unlock();
            }
        }
    }

    public List<Result<T>> getResultList() {
        this.successLock.lock();
        this.failLock.lock();
        try {
            if (this.resultList.size() != this.successOrdinalMap.size() + this.failOrdinalMap.size()) {
                Map<Integer, Result<T>> resultMap = new TreeMap<>();
                for (Integer index : this.successOrdinalMap.keySet()) {
                    resultMap.put(index, new Result<T>(index, this.successOrdinalMap.get(index), true));
                }
                for (Integer index : this.failOrdinalMap.keySet()) {
                    resultMap.put(index, new Result<T>(index, this.failOrdinalMap.get(index), false));
                }
                for (Integer index : resultMap.keySet()) {
                    this.resultList.add(resultMap.get(index));
                }
            }
            return this.resultList;
        } finally {
            this.successLock.unlock();
            this.failLock.unlock();
        }
    }

    public int getSuccessCount() {
        return this.successOrdinalMap.size();
    }

    public int getFailCount() {
        return this.failOrdinalMap.size();
    }

    public static class Result<T> {

        private final int index;

        private final T value;

        private final boolean isSuccess;

        public Result(int index, T value, boolean isSuccess) {
            this.index = index;
            this.value = value;
            this.isSuccess = isSuccess;
        }

        public int getIndex() {
            return index;
        }

        public T getValue() {
            return value;
        }

        public boolean isSuccess() {
            return isSuccess;
        }

        public boolean isFail() {
            return !isSuccess;
        }
    }
}